#include "GLCommon.h"
#include "Texture2D.h"
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_img.h"


void FTexture2D::BindTextureUnit(int8_t UnitIndex)
{
	glActiveTexture(GL_TEXTURE0 + UnitIndex);
	glBindTexture(GL_TEXTURE_2D, TextureHandle);
}

uint32_t FTexture2D::GetHandle() const
{
	return  TextureHandle;
}

void FTexture2D::GenerateMipmap() const
{
	glBindTexture(GL_TEXTURE_2D, TextureHandle);
	glGenerateMipmap(GL_TEXTURE_2D);
}

std::shared_ptr<FTexture2D> FTextureBuild::Build()
{
	uint32_t TextureTarget = 0;
	glGenTextures(1, &TextureTarget);
	glBindTexture(GL_TEXTURE_2D, TextureTarget);


	const auto WrapAroundValue = WrapAround.value_or(ETextureTypes::WrapAround::Repeat);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, WrapAroundValue);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, WrapAroundValue);

	if (WrapAroundValue == ETextureTypes::WrapAround::ClampToBorder)
	{
		glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, glm::value_ptr(BorderColor));
	}


	const auto FilterValue = Filter.value_or(ETextureTypes::Filter::Linear);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, FilterValue);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, FilterValue);


	uint8_t* ImgDataPtr = nullptr;
	if (ImgFilePath.has_value())
	{
		int32_t Channels = 0;
		stbi_set_flip_vertically_on_load(ImgFileFlipVertically);
		ImgDataPtr = stbi_load(ImgFilePath->string().c_str(), &Width, &Height, &Channels, 0);
		if (!ImgDataPtr)
		{
			return nullptr;
		}

		Data = ImgDataPtr;
		DataType = GL_UNSIGNED_BYTE;
		Format = InternalFormat = (Channels == 4 ? GL_RGBA : GL_RGB);
	}

	glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Width, Height, 0, Format, DataType, Data);

	if (GenMinMap)
	{
		glGenerateMipmap(GL_TEXTURE_2D);
	}

	auto P = std::make_shared<FTexture2D>();
	P->TextureHandle = TextureTarget;
	P->Width = Width;
	P->Height = Height;

	if (ImgDataPtr)
	{
		stbi_image_free(ImgDataPtr);
	}

	return P;
}

std::shared_ptr<FTexture2D> FTextureBuild::Build(const FFileSys::path& FilePath, bool FlipVertically)
{
	this->SetImgFileInfo(FilePath, FlipVertically);
	this->SetGenMinMap(true);

	return this->Build();
}

FTextureBuild& FTextureBuild::SetTextureData(const int32_t InType, void* InData)
{
	Data = InData;
	DataType = InType;
	return *this;
}

FTextureBuild& FTextureBuild::SetFormat(const int32_t InFormat)
{
	Format = InFormat;
	return *this;
}

FTextureBuild& FTextureBuild::SetInternalFormat(const int32_t InFormat)
{
	InternalFormat = InFormat;
	return *this;
}

FTextureBuild& FTextureBuild::SetSize(const int32_t W, const int32_t H)
{
	Width = W;
	Height = H;
	return *this;
}

FTextureBuild& FTextureBuild::SetFilter(const ETextureTypes::Filter InFilter)
{
	Filter = InFilter;
	return *this;
}

FTextureBuild& FTextureBuild::SetWrapAround(const ETextureTypes::WrapAround InWrapAround)
{
	WrapAround = InWrapAround;
	return *this;
}

auto FTextureBuild::SetImgFileInfo(const FFileSys::path& Path, bool FlipVertically) -> FTextureBuild&
{
	this->ImgFilePath = Path;
	this->ImgFileFlipVertically = FlipVertically;
	return *this;
}

auto FTextureBuild::SetGenMinMap(bool Enabled) -> FTextureBuild&
{
	this->GenMinMap = Enabled;
	return *this;
}

void FTextureBuild::Reset()
{
	*this = FTextureBuild();
}

